using System;
using System.Collections;
using System.Collections.Generic;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using Microsoft.Practices.CompositeUI;
using Microsoft.Practices.CompositeUI.SmartParts;
using NUnit.Framework;
using Rhino.Mocks;
using SCSFContrib.CompositeUI.WPF.Workspaces;

namespace SCSFContrib.CompositeUI.WPF.UnitTest.Workspaces
{
	[TestFixture]
	public sealed class TabWorkspaceTest : UnitTest
	{
		private TabWorkspace _tabWorkspace;

		protected override void SetUpCore()
		{
			base.SetUpCore();
			_tabWorkspace = new TabWorkspace();
		}

		[Test]
		public void InjectDependencies_ShouldProvideWorkItemToComposer()
		{
			TestableRootWorkItem workItem = new TestableRootWorkItem();
			_tabWorkspace.InjectDependencies(workItem);
			Assert.AreSame(workItem, GetPrivateMemberValue<WorkItem>(typeof(Microsoft.Practices.CompositeUI.SmartParts.Workspace<object, TabSmartPartInfo>), GetWorkspaceComposer(), "workItem"));
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnApplySmartPartInfo_ShouldThrowIfSmartPartIsNull()
		{
			_tabWorkspace.OnApplySmartPartInfo(null, new TabSmartPartInfo());
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnApplySmartPartInfo_ShouldThrowIfSmartPartInfoIsNull()
		{
			_tabWorkspace.OnApplySmartPartInfo("abc", null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "smartPart is not present in this workspace.\r\nParameter name: smartPart")]
		public void OnApplySmartPartInfo_ShouldThrowIfSmartPartIsNotFoundInWorkspace()
		{
			_tabWorkspace.OnApplySmartPartInfo("abc", new TabSmartPartInfo());
		}

		[Test]
		public void OnApplySmartPartInfo_ShouldApplyInfoToRelatedTabItem()
		{
			string smartPart = "abc";
			TabSmartPartInfo smartPartInfo = new TabSmartPartInfo();

			_tabWorkspace.Show(smartPart, smartPartInfo);
			TabItem tabItem = _tabWorkspace.TabItems[smartPart];

			smartPartInfo.Header = "header";

			Assert.AreEqual(string.Empty, tabItem.Header);
			_tabWorkspace.ApplySmartPartInfo(smartPart, smartPartInfo);
			Assert.AreEqual("header", tabItem.Header);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnShow_ShouldThrowIfSmartPartIsNull()
		{
			_tabWorkspace.OnShow(null, new TabSmartPartInfo());
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnShow_ShouldThrowIfSmartPartInfoIsNull()
		{
			_tabWorkspace.OnShow("abc", null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "smartPart is already present in this workspace.\r\nParameter name: smartPart")]
		public void OnShow_ShouldThrowIfSmartPartIsFoundInWorkspace()
		{
			_tabWorkspace.Show("abc");
			_tabWorkspace.OnShow("abc", new TabSmartPartInfo());
		}

		[Test]
		public void OnShow_ShouldShowSmartPartInTabItem()
		{
			string smartPart = "abc";

			Assert.IsEmpty(_tabWorkspace.TabItems.Keys as System.Collections.ICollection);
			_tabWorkspace.Show(smartPart);
			Assert.IsNotEmpty(_tabWorkspace.TabItems.Keys as System.Collections.ICollection);
			Assert.IsTrue(_tabWorkspace.TabItems.ContainsKey(smartPart));
			TabItem tabItem = _tabWorkspace.TabItems[smartPart];
			Assert.AreEqual(smartPart, tabItem.Content);
			Assert.AreSame(smartPart, (_tabWorkspace.Items[0] as TabItem).Content);
		}

		[Test]
		public void OnShow_ShouldInsertTabAtBeginningIfRequested()
		{
			string smartPart1 = "abc";
			string smartPart2 = "def";
			string smartPart3 = "ghi";

			TabSmartPartInfo smartPartInfo = new TabSmartPartInfo();
			smartPartInfo.Position = TabPosition.Beginning;

			Assert.IsEmpty(_tabWorkspace.TabItems.Keys as System.Collections.ICollection);
			_tabWorkspace.Show(smartPart1, smartPartInfo);
			_tabWorkspace.Show(smartPart2, smartPartInfo);
			_tabWorkspace.Show(smartPart3, smartPartInfo);

			Assert.IsNotEmpty(_tabWorkspace.TabItems.Keys as System.Collections.ICollection);
			Assert.IsTrue(_tabWorkspace.TabItems.ContainsKey(smartPart1));
			Assert.IsTrue(_tabWorkspace.TabItems.ContainsKey(smartPart2));
			Assert.IsTrue(_tabWorkspace.TabItems.ContainsKey(smartPart3));
			Assert.AreSame(smartPart3, (_tabWorkspace.Items[0] as TabItem).Content);
			Assert.AreSame(smartPart2, (_tabWorkspace.Items[1] as TabItem).Content);
			Assert.AreSame(smartPart1, (_tabWorkspace.Items[2] as TabItem).Content);
		}

		[Test]
		public void OnShow_ShouldNotActivateTabIfRequested()
		{
			string smartPart1 = "abc";
			string smartPart2 = "def";
			string smartPart3 = "ghi";

			TabSmartPartInfo smartPartInfo = new TabSmartPartInfo();
			smartPartInfo.Activate = false;

			Assert.IsEmpty(_tabWorkspace.TabItems.Keys as System.Collections.ICollection);
			_tabWorkspace.Show(smartPart1);
			Assert.AreSame(smartPart1, _tabWorkspace.SelectedContent);
			_tabWorkspace.Show(smartPart2, smartPartInfo);
			Assert.AreSame(smartPart1, _tabWorkspace.SelectedContent);
			_tabWorkspace.Show(smartPart3);
			Assert.AreSame(smartPart3, _tabWorkspace.SelectedContent);
		}

		[Test]
		public void OnShow_ShouldUseHeaderIfSupplied()
		{
			string smartPart = "abc";
			TextBlock header = new TextBlock();
			TabSmartPartInfo smartPartInfo = new TabSmartPartInfo(header);
			smartPartInfo.Title = "title";

			_tabWorkspace.Show(smartPart, smartPartInfo);
			Assert.IsNotEmpty(_tabWorkspace.TabItems.Keys as System.Collections.ICollection);
			Assert.AreSame(header, (_tabWorkspace.Items[0] as TabItem).Header);
		}


		[Test]
		public void OnShow_ShouldUseTitleForHeaderIfNoHeaderSupplied()
		{
			string smartPart = "abc";
			TabSmartPartInfo smartPartInfo = new TabSmartPartInfo();
			smartPartInfo.Title = "title";

			_tabWorkspace.Show(smartPart, smartPartInfo);
			Assert.IsNotEmpty(_tabWorkspace.TabItems.Keys as System.Collections.ICollection);
			Assert.AreEqual("title", (_tabWorkspace.Items[0] as TabItem).Header);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnActivate_ShouldThrowIfSmartPartIsNull()
		{
			_tabWorkspace.OnActivate(null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "smartPart is not present in this workspace.\r\nParameter name: smartPart")]
		public void OnActivate_ShouldThrowIfSmartPartIsNotFoundInWorkspace()
		{
			_tabWorkspace.OnActivate("abc");
		}

		[Test]
		public void OnActivate_ShouldSelectRelatedTabItemForSmartPart()
		{
			string smartPart1 = "abc";
			string smartPart2 = "def";
			string smartPart3 = "ghi";

			_tabWorkspace.Show(smartPart1);
			_tabWorkspace.Show(smartPart2);
			_tabWorkspace.Show(smartPart3);

			Assert.AreSame(smartPart3, _tabWorkspace.SelectedContent);
			_tabWorkspace.Activate(smartPart1);
			Assert.AreSame(smartPart1, _tabWorkspace.SelectedContent);
			_tabWorkspace.Activate(smartPart2);
			Assert.AreSame(smartPart2, _tabWorkspace.SelectedContent);
		}

		[Test]
		public void OnActivate_ShouldRedisplayHiddenTabItem()
		{
			string smartPart1 = "abc";
			string smartPart2 = "def";

			_tabWorkspace.Show(smartPart1);
			_tabWorkspace.Show(smartPart2);
			_tabWorkspace.Hide(smartPart2);

			Assert.AreEqual(Visibility.Collapsed, _tabWorkspace.TabItems[smartPart2].Visibility);
			_tabWorkspace.Activate(smartPart2);
			Assert.AreEqual(Visibility.Visible, _tabWorkspace.TabItems[smartPart2].Visibility);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnHide_ShouldThrowIfSmartPartIsNull()
		{
			_tabWorkspace.OnHide(null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "smartPart is not present in this workspace.\r\nParameter name: smartPart")]
		public void OnHide_ShouldThrowIfSmartPartIsNotFoundInWorkspace()
		{
			_tabWorkspace.OnHide("abc");
		}

		[Test]
		public void OnHide_ShouldHideTabItemForSmartPart()
		{
			string smartPart1 = "abc";
			string smartPart2 = "def";
			string smartPart3 = "ghi";

			_tabWorkspace.Show(smartPart1);
			_tabWorkspace.Show(smartPart2);
			_tabWorkspace.Show(smartPart3);

			Assert.AreSame(smartPart3, _tabWorkspace.SelectedContent);
			Assert.AreEqual(Visibility.Visible, _tabWorkspace.TabItems[smartPart3].Visibility);

			_tabWorkspace.Hide(smartPart3);
			Assert.IsNull(_tabWorkspace.SelectedContent);
			Assert.AreEqual(Visibility.Collapsed, _tabWorkspace.TabItems[smartPart3].Visibility);

			_tabWorkspace.Hide(smartPart2);
			Assert.IsNull(_tabWorkspace.SelectedContent);
			Assert.AreEqual(Visibility.Collapsed, _tabWorkspace.TabItems[smartPart2].Visibility);

			_tabWorkspace.Hide(smartPart1);
			Assert.IsNull(_tabWorkspace.SelectedContent);
			Assert.AreEqual(Visibility.Collapsed, _tabWorkspace.TabItems[smartPart1].Visibility);
		}

		[Test]
		[ExpectedException(typeof(ArgumentNullException))]
		public void OnClose_ShouldThrowIfSmartPartIsNull()
		{
			_tabWorkspace.OnClose(null);
		}

		[Test]
		[ExpectedException(typeof(ArgumentException), ExpectedMessage = "smartPart is not present in this workspace.\r\nParameter name: smartPart")]
		public void OnClose_ShouldThrowIfSmartPartIsNotFoundInWorkspace()
		{
			_tabWorkspace.OnClose("abc");
		}

		[Test]
		public void OnClose_ShouldRemoveRelatedTabItem()
		{
			string smartPart1 = "abc";
			string smartPart2 = "def";
			string smartPart3 = "ghi";

			_tabWorkspace.Show(smartPart1);
			_tabWorkspace.Show(smartPart2);
			_tabWorkspace.Show(smartPart3);

			Assert.AreSame(smartPart3, _tabWorkspace.SelectedContent);

			Assert.AreEqual(3, _tabWorkspace.Items.Count);
			_tabWorkspace.Close(smartPart3);
			Assert.IsNull(_tabWorkspace.SelectedContent);
			Assert.AreEqual(2, _tabWorkspace.Items.Count);
			Assert.IsFalse(_tabWorkspace.TabItems.ContainsKey(smartPart3));

			Assert.AreEqual(2, _tabWorkspace.Items.Count);
			_tabWorkspace.Close(smartPart2);
			Assert.IsNull(_tabWorkspace.SelectedContent);
			Assert.AreEqual(1, _tabWorkspace.Items.Count);
			Assert.IsFalse(_tabWorkspace.TabItems.ContainsKey(smartPart2));

			Assert.AreEqual(1, _tabWorkspace.Items.Count);
			_tabWorkspace.Close(smartPart1);
			Assert.IsNull(_tabWorkspace.SelectedContent);
			Assert.AreEqual(0, _tabWorkspace.Items.Count);
			Assert.IsFalse(_tabWorkspace.TabItems.ContainsKey(smartPart1));
		}

		[Test]
		public void OnClose_ShouldRemoveSmartPartFromTabItem()
		{
			UserControl smartPart = new UserControl();
			_tabWorkspace.Show(smartPart);
			TabItem tabItem = _tabWorkspace.TabItems[smartPart];
			Assert.AreSame(smartPart, tabItem.Content);
			_tabWorkspace.Close(smartPart);
			Assert.IsNull(tabItem.Content);

			_tabWorkspace.Show(smartPart);
			tabItem = _tabWorkspace.TabItems[smartPart];
			Assert.AreSame(smartPart, tabItem.Content);
		}

		[Test]
		public void ActiveSmartPart_ShouldUpdateAsTabItemsAreSelected()
		{
			string smartPart1 = "abc";
			string smartPart2 = "def";
			string smartPart3 = "ghi";

			_tabWorkspace.Show(smartPart1);
			_tabWorkspace.Show(smartPart2);
			_tabWorkspace.Show(smartPart3);

			Assert.AreSame(smartPart3, _tabWorkspace.ActiveSmartPart);
			(_tabWorkspace.Items[0] as TabItem).IsSelected = true;
			Assert.AreSame(smartPart1, _tabWorkspace.ActiveSmartPart);
			(_tabWorkspace.Items[1] as TabItem).IsSelected = true;
			Assert.AreSame(smartPart2, _tabWorkspace.ActiveSmartPart);
			(_tabWorkspace.Items[2] as TabItem).IsSelected = true;
			Assert.AreSame(smartPart3, _tabWorkspace.ActiveSmartPart);
			_tabWorkspace.SelectedItem = null;
			Assert.IsNull(_tabWorkspace.SelectedContent);
		}

		[Test]
		public void OnSelectionChanged_ShouldIgnoreTabItemsThatArentInWorkspace()
		{
			_tabWorkspace.Show("abc");
			Assert.AreEqual("abc", _tabWorkspace.ActiveSmartPart);

			TabItem tabItem = new TabItem();
			tabItem.Content = "some content that isn't a know smart part";
			_tabWorkspace.Items.Add(tabItem);
			_tabWorkspace.SelectedItem = tabItem;
			Assert.IsNull(_tabWorkspace.ActiveSmartPart);
		}

		#region Composable Workspace Tests

		[Test]
		public void ActiveSmartPart_ShouldDelegateToComposer()
		{
			Assert.IsNull(_tabWorkspace.ActiveSmartPart);
			Assert.AreSame(GetWorkspaceComposer().ActiveSmartPart, _tabWorkspace.ActiveSmartPart);

			GetWorkspaceComposer().InnerSmartParts.Add("abc");
			GetWorkspaceComposer().SetActiveSmartPart("abc");

			Assert.IsNotNull(_tabWorkspace.ActiveSmartPart);
			Assert.AreSame(GetWorkspaceComposer().ActiveSmartPart, _tabWorkspace.ActiveSmartPart);
		}

		[Test]
		public void SmartParts_ShouldDelegateToComposer()
		{
			Assert.IsEmpty(_tabWorkspace.SmartParts);

			GetWorkspaceComposer().InnerSmartParts.Add("abc");
			GetWorkspaceComposer().SetActiveSmartPart("abc");

			Assert.IsNotEmpty(_tabWorkspace.SmartParts);
			Assert.AreSame("abc", _tabWorkspace.SmartParts[0]);
		}

		[Test]
		public void InnerSmartParts_ShouldDelegateToComposer()
		{
			Assert.IsEmpty(GetPrivateMemberValue<IList<object>>(_tabWorkspace, "InnerSmartParts") as ICollection);

			GetWorkspaceComposer().InnerSmartParts.Add("abc");
			GetWorkspaceComposer().SetActiveSmartPart("abc");

			Assert.IsNotEmpty(GetPrivateMemberValue<IList<object>>(_tabWorkspace, "InnerSmartParts") as ICollection);
			Assert.AreSame("abc", GetPrivateMemberValue<IList<object>>(_tabWorkspace, "InnerSmartParts")[0]);
		}

		[Test]
		public void Activate_ShouldDelegateToComposerAndInvokeOnActivate()
		{
			_tabWorkspace = Mocks.PartialMock<TabWorkspace>();
			_tabWorkspace.OnActivate("abc");
			LastCall.Repeat.Once();
			Mocks.ReplayAll();

			GetWorkspaceComposer().InnerSmartParts.Add("abc");
			_tabWorkspace.Activate("abc");
		}

		[Test]
		public void ApplySmartPartInfo_ShouldDelegateToComposerAndInvokeOnApplySmartPartInfo()
		{
			_tabWorkspace = Mocks.PartialMock<TabWorkspace>();
			TabSmartPartInfo smartPartInfo = new TabSmartPartInfo();
			_tabWorkspace.OnApplySmartPartInfo("abc", smartPartInfo);
			LastCall.Repeat.Once();
			Mocks.ReplayAll();

			GetWorkspaceComposer().InnerSmartParts.Add("abc");
			_tabWorkspace.ApplySmartPartInfo("abc", smartPartInfo);
		}

		[Test]
		public void Close_ShouldDelegateToComposerAndInvokeOnClose()
		{
			_tabWorkspace = Mocks.PartialMock<TabWorkspace>();
			_tabWorkspace.OnClose("abc");
			LastCall.Repeat.Once();
			Mocks.ReplayAll();

			GetWorkspaceComposer().InnerSmartParts.Add("abc");
			_tabWorkspace.Close("abc");
		}

		[Test]
		public void Hide_ShouldDelegateToComposerAndInvokeOnHide()
		{
			_tabWorkspace = Mocks.PartialMock<TabWorkspace>();
			_tabWorkspace.OnHide("abc");
			LastCall.Repeat.Once();
			Mocks.ReplayAll();

			GetWorkspaceComposer().InnerSmartParts.Add("abc");
			_tabWorkspace.Hide("abc");
		}

		[Test]
		public void Show_ShouldDelegateToComposerAndInvokeOnShow()
		{
			_tabWorkspace = Mocks.PartialMock<TabWorkspace>();
			_tabWorkspace.OnShow("abc", null);
			LastCall.IgnoreArguments().Repeat.Once();
			Mocks.ReplayAll();

			_tabWorkspace.Show("abc");
		}

		[Test]
		public void Show_SmartPartInfo_ShouldDelegateToComposerAndInvokeOnShow()
		{
			_tabWorkspace = Mocks.PartialMock<TabWorkspace>();
			TabSmartPartInfo smartPartInfo = new TabSmartPartInfo();
			_tabWorkspace.OnShow("abc", smartPartInfo);
			LastCall.Repeat.Once();
			Mocks.ReplayAll();

			_tabWorkspace.Show("abc", smartPartInfo);
		}

		[Test]
		public void ConvertFrom_ShouldInvokeOnConvertFrom()
		{
			TabSmartPartInfo smartPartInfo = new TabSmartPartInfo();
			_tabWorkspace = Mocks.DynamicMock<TabWorkspace>();
			InvokePrivateMethod<TabSmartPartInfo>(_tabWorkspace, "OnConvertFrom", smartPartInfo);
			LastCall.Repeat.Once().Return(smartPartInfo);
			Mocks.ReplayAll();

			(_tabWorkspace as IComposableWorkspace<object, TabSmartPartInfo>).ConvertFrom(smartPartInfo);
		}

		[Test]
		public void ConvertFrom_ShouldCopyTabPropertiesIfSourceIsTabSmartPartInfo()
		{
			TabSmartPartInfo source = new TabSmartPartInfo();
			source.Title = "title";
			source.Description = "desc";
			source.Header = "header";
			source.Activate = false;
			source.Position = TabPosition.Beginning;

			TabSmartPartInfo tabSmartPartInfo = InvokePrivateMethod<TabSmartPartInfo>(_tabWorkspace, "OnConvertFrom", source);
			Assert.AreEqual("title", tabSmartPartInfo.Title);
			Assert.AreEqual("desc", tabSmartPartInfo.Description);
			Assert.AreEqual("header", tabSmartPartInfo.Header);
			Assert.IsFalse(tabSmartPartInfo.Activate);
			Assert.AreEqual(TabPosition.Beginning, tabSmartPartInfo.Position);
		}

		[Test]
		public void RaiseSmartPartActivated_ShouldInvokeOnRaiseSmartPartActivated()
		{
			WorkspaceEventArgs e = new WorkspaceEventArgs("abc");
			_tabWorkspace = Mocks.DynamicMock<TabWorkspace>();
			InvokePrivateMethod<SmartPartInfo>(_tabWorkspace, "OnSmartPartActivated", e);
			LastCall.Repeat.Once();
			Mocks.ReplayAll();

			(_tabWorkspace as IComposableWorkspace<object, TabSmartPartInfo>).RaiseSmartPartActivated(e);
		}

		[Test]
		public void RaiseSmartPartClosing_ShouldInvokeOnRaiseSmartPartClosing()
		{
			WorkspaceCancelEventArgs e = new WorkspaceCancelEventArgs("abc");
			_tabWorkspace = Mocks.DynamicMock<TabWorkspace>();
			InvokePrivateMethod<SmartPartInfo>(_tabWorkspace, "OnSmartPartClosing", e);
			LastCall.Repeat.Once();
			Mocks.ReplayAll();

			(_tabWorkspace as IComposableWorkspace<object, TabSmartPartInfo>).RaiseSmartPartClosing(e);
		}

		[Test]
		public void OnSmartPartActivated_ShouldRaiseSmartPartActivated()
		{
			Mocks.ReplayAll();
			bool raised = false;

			InvokePrivateMethod<object>(_tabWorkspace, "OnSmartPartActivated", new WorkspaceEventArgs("abc"));
			Assert.IsFalse(raised);

			_tabWorkspace.SmartPartActivated += delegate
			{
				raised = true;
			};

			InvokePrivateMethod<object>(_tabWorkspace, "OnSmartPartActivated", new WorkspaceEventArgs("abc"));
			Assert.IsTrue(raised);
		}

		[Test]
		public void OnSmartPartClosing_ShouldRaiseSmartPartClosing()
		{
			Mocks.ReplayAll();
			bool raised = false;

			InvokePrivateMethod<object>(_tabWorkspace, "OnSmartPartClosing", new WorkspaceCancelEventArgs("abc"));
			Assert.IsFalse(raised);

			_tabWorkspace.SmartPartClosing += delegate
			{
				raised = true;
			};

			InvokePrivateMethod<object>(_tabWorkspace, "OnSmartPartClosing", new WorkspaceCancelEventArgs("abc"));
			Assert.IsTrue(raised);
		}

		[Test]
		public void ConvertFrom_ShouldInvokeDefaultConvertBehavior()
		{
			Mocks.ReplayAll();
			SmartPartInfo source = new SmartPartInfo();
			source.Title = "title";
			source.Description = "desc";
			SmartPartInfo copy = (_tabWorkspace as IComposableWorkspace<object, TabSmartPartInfo>).ConvertFrom(source);
			Assert.AreEqual("title", copy.Title);
			Assert.AreEqual("desc", copy.Description);
		}

		#endregion

		#region Support Methods

		private SCSFContrib.CompositeUI.WPF.Workspaces.WorkspaceComposer<object, TabSmartPartInfo> GetWorkspaceComposer()
		{
			FieldInfo fieldInfo = typeof(TabWorkspace).GetField("_composer", BindingFlags.NonPublic | BindingFlags.Instance);
			Assert.IsNotNull(fieldInfo, "_composer field not found - may have been renamed.");
			SCSFContrib.CompositeUI.WPF.Workspaces.WorkspaceComposer<object, TabSmartPartInfo> workspaceComposer = fieldInfo.GetValue(_tabWorkspace) as SCSFContrib.CompositeUI.WPF.Workspaces.WorkspaceComposer<object, TabSmartPartInfo>;
			Assert.IsNotNull(workspaceComposer, "WorkspaceComposer could not be retrieved from _composer field.");
			return workspaceComposer;
		}

		#endregion
	}
}
